Option Explicit

' ======================================================================
' Descriptor
' 范围列对象
' ======================================================================

' ======================================================================
' Implement
' ======================================================================

Implements ISqlFormatterElem

' ======================================================================
' Field
' ======================================================================

' ==================================================
' Field
' ==================================================
' 用来所有组成元素: 子列对象 + 运算符
' IObjArrayRef<ISqlFormatterElem>
Private componentArray As IObjArrayRef

' ==================================================
' ISqlFormatterElem Field
' ==================================================
' 前缀空格的数量
' 默认值 = 0
Private prefixSpaceCount As Integer

' 左侧括号的数量
' 默认值 = 0
Private leftParenthesisCount As Integer
' 右侧括号的数量
' 默认值 = 0
Private rightParenthesisCount As Integer

' 当前对象是否是多行的状态
' 默认值 = False
Private multiLineState As Boolean
' 元素数组是否是以注释结尾
' 默认值 = False
Private componentArrayIsEndsWithComment As Boolean

' ======================================================================
' Constructor
' ======================================================================
Private Sub Class_Initialize()
    ' 组成条件的所有元素
    Set componentArray = UtilsArrayRef.createRealObjArrayRef(8)
End Sub

Private Sub Class_Terminate()
    Set componentArray = Nothing
End Sub

' ======================================================================
' implements ISqlFormatterElem Function
' ======================================================================
' ==================================================
' ISqlFormatterElem -- 添加组成元素
' ==================================================
' 添加组成元素
Public Sub ISqlFormatterElem_addComponent(ByRef c As ISqlFormatterElem)
    ' ==================================================
    ' 1. 参数检查
    ' ==================================================
    ' 1. 检查: 如果元素是 `null` 则退出
    If c Is Nothing Then
        Exit Sub
    End If

    ' ==================================================
    ' 2. 保存元素
    ' ==================================================
    ' 1. 将元素保存到元素数组中
    componentArray.Add c

    With c ' -------> 在这里加速
        ' ==================================================
        ' 3. 设置当前对象的组成元素是不是多行的状态
        ' ==================================================
        ' 1. 检查: 如果当前对象不是多行的状态 `multiLineState == false`，则继续
        If multiLineState = False Then
            ' 2. 检查: 如果当前元素是多行的，则设置 `multiLineState = true`
            If .isMultiLine Then
                multiLineState = True

            ' 3. 否则，检查: 如果元素数组是以注释结尾的 `componentArrayIsEndsWithComment == true`，则设置 `multiLineState = true`
            ElseIf componentArrayIsEndsWithComment Then
                multiLineState = True

            ' 4. 否则，检查: 如果当前元素是单独一行的注释
            ElseIf .isSingleLineComment Then
                ' 4.1. 如果元素数组中的元素数量 > 1 ，则设置 `multiLineState = true`
                If componentArray.Count > 1 Then
                    multiLineState = True
                End If
            End If
        End If

        ' ==================================================
        ' 4. 保存元素数组是否是以注释结尾的
        ' ==================================================
        ' 1. 将当前元素的是否以注释结尾的结果: `isEndsWithComment() == true` 保存到属性 `componentArrayIsEndsWithComment`
        componentArrayIsEndsWithComment = .isEndsWithComment
    End With
End Sub

' 将元素添加到开头位置
Public Sub ISqlFormatterElem_addComponentToFirst(ByRef c As ISqlFormatterElem)

    ' ==================================================
    ' 1. 参数检查
    ' ==================================================
    ' 1. 检查: 如果元素是 `null` 则退出
    If c Is Nothing Then
        Exit Sub
    End If

    With c ' -------> 在这里加速
        ' ==================================================
        ' 2. 保存元素
        ' ==================================================
        ' 1. 将元素添加到开头位置
        componentArray.AddFirst c

        ' 2. 获取元素数量 和 当前元素是否是以注释结尾
        Dim componentArrayCount As Long
        componentArrayCount = componentArray.Count

        Dim curIsEndsWithComment As Boolean
        curIsEndsWithComment = .isEndsWithComment

        ' ==================================================
        ' 3. 设置当前对象的组成元素是不是多行的状态
        ' ==================================================
        ' 1. 检查: 如果当前对象不是多行的状态 `multiLineState == false`，则继续
        If multiLineState = False Then
            ' 2. 检查: 如果当前元素是多行的，则设置 `multiLineState = true`
            If .isMultiLine Then
                multiLineState = True

            ' 3. 否则，检查: 如果元素数量 > 1
            ElseIf componentArrayCount > 1 Then
                ' 3.1. 检查: 如果当前元素是注释结尾，则是多行
                '    (说明在单行的基础行，又增加了一个包含注释的内容，所以变成了多行)
                If curIsEndsWithComment Then
                    multiLineState = True

                ' 3.2. 否则，检查: 如果第二个元素是单行注释，则设置为多行的
                Else
                    Dim tempElem As ISqlFormatterElem
                    Set tempElem = componentArray.Item(1)
                    If tempElem.isSingleLineComment Then
                        multiLineState = True
                    End If
                End If
            End If
            ' 4. 否则 (到这里说明只有一个元素，则不用检查，无论是哪种情况都是单行的)
        End If

        ' ==================================================
        ' 4. 如果当前只有一个元素，则保存: 是否是以注释结尾
        ' ==================================================
        If componentArrayCount = 1 Then
            componentArrayIsEndsWithComment = curIsEndsWithComment
        End If
    End With
End Sub

' 能否将元素添加到开头位置
Public Function ISqlFormatterElem_canAddComponentToFirst() As Boolean
    ' 不存在左边界括号时，可以将元素添加到开头
    ISqlFormatterElem_canAddComponentToFirst = (leftParenthesisCount <= 0)
End Function

' ==================================================
' ISqlFormatterElem -- 对齐处理
' ==================================================
' 该方法是用来配置对齐的方法
' 返回元素内，最多一级深度下的对齐元素组
' 如果是列对象，则返回作用域各个组成元素内的条件组（主要是子查询）
' 如果是条件对象，则返回条件自身
' 如果是作用域对象，则返回作用域内各部分中的条件组合
' 如果是赋值语句，则返回语句自身
' 如果没有条件或者赋值语句，则返回 null
'
' @return IObjArrayRef<IObjArrayRef<ISqlFormatterAlignmentElem>>
Public Function ISqlFormatterElem_getAlignmentElemGroups() As IObjArrayRef
    ' Dim result As IObjArrayRef
    Dim groups As IObjArrayRef

    Dim temp As ISqlFormatterElem

    Dim i As Long
    For i = 0 To componentArray.MaxIndex
        Set temp = componentArray.Item(i)

        Set groups = temp.getAlignmentElemGroups
        If Not groups Is Nothing Then
            If ISqlFormatterElem_getAlignmentElemGroups Is Nothing Then
                Set ISqlFormatterElem_getAlignmentElemGroups = UtilsArrayRef.createRealObjArrayRef
            End If

            ISqlFormatterElem_getAlignmentElemGroups.mergeArrRef groups
        End If
    Next

    ' return result --> ISqlFormatterElem_getAlignmentElemGroups
End Function

' ==================================================
' ISqlFormatterElem -- 前缀空格设置
' ==================================================
' 设置当前元素的嵌套深度，并计算前缀空格的数量
' 会覆盖 setPrefixSpaceCount 的设置结果
Public Sub ISqlFormatterElem_setElemNestCount(ByVal nestCount As Integer, ByRef config As SqlFormatterConfiguration)
    ' 1. 保存当前的列对象的数量
    prefixSpaceCount = nestCount * config.elemNestLevelPrefixSpaceCount

    ' 2. 检查: 如果存在边界括号，则嵌套深度 + 1
    Dim subColNestCount As Integer
    subColNestCount = nestCount
    If ISqlFormatterElem_hasBoundaryParenthesis Then
        subColNestCount = subColNestCount + 1
    End If

    ' 3. 设置每个子列的嵌套深度
    Dim temp As ISqlFormatterElem
    Dim i As Long
    For i = 0 To componentArray.MaxIndex
        Set temp = componentArray.Item(i)
        temp.setElemNestCount subColNestCount, config
    Next
End Sub

' 直接设置前缀空格的数量
' 将会覆盖 setElemNestCount 对前缀空格数量的控制
Public Sub ISqlFormatterElem_setPrefixSpaceCount(ByVal spaceCount As Integer, ByRef config As SqlFormatterConfiguration)
    ' 1. 保存当前的列对象的数量
    prefixSpaceCount = spaceCount

    ' 2. 检查: 如果存在边界括号，则 + 一个单位的空格
    Dim subColPrefixSpaceCount As Integer
    subColPrefixSpaceCount = spaceCount
    If ISqlFormatterElem_hasBoundaryParenthesis Then
        subColPrefixSpaceCount = subColPrefixSpaceCount + config.elemNestLevelPrefixSpaceCount
    End If

    ' 3. 设置每个子列的嵌套深度
    Dim temp As ISqlFormatterElem
    Dim i As Long
    For i = 0 To componentArray.MaxIndex
        Set temp = componentArray.Item(i)
        temp.setPrefixSpaceCount subColPrefixSpaceCount, config
    Next
End Sub

' ==================================================
' ISqlFormatterElem -- 边界括号
' ==================================================
' 是否存在边界括号，有左边或者右边都返回 true
Public Function ISqlFormatterElem_hasBoundaryParenthesis() As Boolean
    If leftParenthesisCount > 0 Then
        ISqlFormatterElem_hasBoundaryParenthesis = True
    ElseIf rightParenthesisCount > 0 Then
        ISqlFormatterElem_hasBoundaryParenthesis = True
    End If

    ' return False
End Function

' 边界处的 [(] 的数量 + count 个
Public Sub ISqlFormatterElem_addBoundaryLeftParenthesis(Optional ByVal count As Integer = 1)
    leftParenthesisCount = leftParenthesisCount + count
End Sub

' 边界处的 [)] 的数量 + count 个
Public Sub ISqlFormatterElem_addBoundaryRightParenthesis(Optional ByVal count As Integer = 1)
    rightParenthesisCount = rightParenthesisCount + count
End Sub

' ==================================================
' ISqlFormatterElem -- 内容检查
' ==================================================
' 获取类型
Public Function ISqlFormatterElem_getType() As SqlTokenType
    ISqlFormatterElem_getType = SqlTokenType.NO_MEAN
End Function

' 检查当前元素是不是一个注释
Public Function ISqlFormatterElem_isComment() As Boolean
    ' return false
End Function

' 检查当前元素是不是和前一个元素处于同一行的注释
Public Function ISqlFormatterElem_isShareLineComment() As Boolean
    ' return false
End Function

' 检查当前元素是不是处于单独一行的注释
Public Function ISqlFormatterElem_isSingleLineComment() As Boolean
    ' return false
End Function

' 检查当前元素是不是以注释结尾
Public Function ISqlFormatterElem_isEndsWithComment() As Boolean
    ' 1. 如果存在右括号，则默认不是以注释结尾
    If rightParenthesisCount > 0 Then
        ' return false
        Exit Function
    End If

    ' 2. 返回元素数组是否以注释结尾
    ISqlFormatterElem_isEndsWithComment = componentArrayIsEndsWithComment
End Function

' 检查当前生成器是不是多行的
Public Function ISqlFormatterElem_isMultiLine() As Boolean
    ' 检查: 如果已经是多行的，则返回 true
    If multiLineState Then
        ISqlFormatterElem_isMultiLine = True
        Exit Function
    End IF

    ' 检查: 如果最后一个组成元素以注释结尾，则继续
    If componentArrayIsEndsWithComment Then
        ' 如果最后一个组成元素是以注释结尾，则如果还存在边界括号，就是多行的
        ISqlFormatterElem_isMultiLine = ISqlFormatterElem_hasBoundaryParenthesis
    End If

    ' 否则: 返回 false
    ' return false
End Function

' ==================================================
' ISqlFormatterElem -- 生成列的字符串
' ==================================================
' 生成一整行的字符串。如果本身是多行，将会使用换行符进行连接
Public Function ISqlFormatterElem_toString(ByRef config As SqlFormatterConfiguration) As String
    If ISqlFormatterElem_isMultiLine = False Then
        ' 1. 如果是单行的，则直接生成单行字符串
        ISqlFormatterElem_toString = doCreateSingleLineString(config)
        Exit Function
    Else
        ' 1. 生成原始字符集合
        Dim arrRef As IPrimitiveArrayRef
        Set arrRef = doCreateMultiLineArray(config)
        ' 2. 使用换行符连接
        ISqlFormatterElem_toString = UtilsArrayRef.reducePrimitiveArrayRefToStr(arrRef, config.lineBreak)
        Exit Function
    End If
End Function

' 生成多行字符串
' 如果只有一行，则返回的数组引用中将会只有一个
' 并且不会在开头附加前缀空格
'
' @return IPrimitiveArrayRef<String>
Public Function ISqlFormatterElem_toStringArray(ByRef config As SqlFormatterConfiguration) As IPrimitiveArrayRef
    ' ==================================================
    ' 1. 处理单行的情况
    ' ==================================================
    ' 1. 检查: 如果是单行的，则生成单行字符串
    If ISqlFormatterElem_isMultiLine = False Then
        Dim singleStr As String
        singleStr = doCreateSingleLineString(config)

        ' 2. 将 `singleStr` 包装成一个长度为 1 的结果集合，并返回
        Set ISqlFormatterElem_toStringArray = UtilsArrayRef.wrapperToPrimitiveArrayRef(singleStr)
        Exit Function
    End If

    ' ==================================================
    ' 2. 处理多行的情况
    ' ==================================================
    ' 1. 生成多行字符串集合，返回生成结果
    Set ISqlFormatterElem_toStringArray = doCreateMultiLineArray(config)
End Function

' ======================================================================
' Private Function
' ======================================================================
' 生成单行字符串
Private Function doCreateSingleLineString(ByRef config As SqlFormatterConfiguration) As String
    doCreateSingleLineString = SqlFormatterHelper.createFormatSingleStr( _
        componentArray, config, leftParenthesisCount, rightParenthesisCount)
End Function

' 获取原始的字符串集合
' 不包含前缀空格
' 不包含换行符，所以在产生多行的字符串时，返回一个数组集合
' @return IPrimitiveArrayRef<String>
Private Function doCreateMultiLineArray(ByRef config As SqlFormatterConfiguration) As IPrimitiveArrayRef
    ' ==================================================
    ' 1. 检查: 如果对象内部是单行状态: `multiLineState == false`
    ' ==================================================
    If multiLineState = False Then
        ' 1. 创建一个长度为 3 的结果集合
        Set doCreateMultiLineArray = UtilsArrayRef.createRealPrimitiveArrayRef(3)

        ' 2. 处理左括号
        ' 2.1. 检查: 如果存在左边界括号: `leftParenthesisCount > 0`，则继续
        If leftParenthesisCount > 0 Then
            ' 2.2. 将 `等量的左括号` 保存到 `result`
            doCreateMultiLineArray.Add UtilsString.repeatLeftParenthesis(leftParenthesisCount)
        End If

        ' 3. 处理组成元素
        ' 3.1. 将 `一个单位的前缀空格 + 组成元素数组组成的一行字符串` 保存到 `result`
        doCreateMultiLineArray.Add config.elemNestLevelPrefixSpace & SqlFormatterHelper.createFormatSingleStr(componentArray, config, 0, 0)

        ' 4. 处理右括号
        ' 4.1. 检查: 如果存在右边界括号: `rightParenthesisCount > 0`，则继续
        If rightParenthesisCount > 0 Then
            ' 4.2. 将 `等量的右括号` 保存到 `result`
            doCreateMultiLineArray.Add UtilsString.repeatRightParenthesis(rightParenthesisCount)
        End If

        ' 5. 退出处理，返回 `result`
        Exit Function

    ' ==================================================
    ' 2. 否则
    ' ==================================================
    Else
        ' 1. 为组成元素数组生成多行字符串集合
        Set doCreateMultiLineArray = SqlFormatterHelper.createElemExclusiveRowStrArr( _
                        componentArray, config, _
                        leftParenthesisCount, rightParenthesisCount)
    End If
End Function